#ifndef PW_PBL_FEATURES_HPP
#define PW_PBL_FEATURES_HPP

#include "PackIter.hpp"
#include "Tuple.hpp"

namespace pw_pbl::features
{
	/// 空特征类型，可转换为任意可默认构造的对象
	struct FDummy
	{
		template <typename T> requires requires { T{}; }
		constexpr operator T() const noexcept { return T{}; }
	};

	/// 模板集，在特征集中可标记多个模板组成的模板复合特征，表示只允许最多包含复合特征中的一个特征
	/// 也可用在函数参数中用于输入模板参数
	template <template <typename...> class... TPL>
	struct tplset
	{};

	/// 类型集，在特征集中可标记多个类型组成的类型复合特征，表示只允许最多包含复合特征中的一个特征
	/// 也可用在函数参数中用于输入类型参数
	template <typename... F>
	struct typset
	{};
} // namespace pw_pbl::features

namespace pw_pbl::_features
{
	using features::FDummy;

	constexpr FDummy EDummy{};
} // namespace pw_pbl::_features

namespace pw_pbl::features
{
	/// 特征集，无序收集特征，用于具体特征集转换为有序特征集并可压缩存储空间
	template <typename... FEATURES>
	struct Features
	{
		Tuple<FEATURES...> data;

		constexpr Features(const Tuple<FEATURES...> &fs) noexcept : data(fs) {}
		constexpr Features(const FEATURES &...fs) noexcept : data({{fs}...}) {}

		static constexpr size_t ECount = sizeof...(FEATURES);

		/// 单个模板特征在本特征集中的索引，不存在时返回ECount
		template <template <typename...> class TMP>
		static constexpr size_t EIndexForTpl = (pack_iter::find << ... << pw_pbl::ccIsTemplate<FEATURES, TMP>);

		/// 多个模板特征组成的复合特征在本特征集中有效特征的索引result和在复合特征中的索引index
		template <template <typename...> class... TMP>
		static constexpr pack_iter::find_result<size_t> ETplsetID = (pack_iter::find_lt<ECount> << ... << EIndexForTpl<TMP>);

		/// 多个模板特征组成的复合特征在本特征集中有效特征的实际类型，如果均未包含则返回FDummy
		template <template <typename...> class... TMP>
		using select_tpl_feature = ttPackElement<ETplsetID<TMP...>.result, FEATURES..., FDummy>;

		/// 多个模板特征组成的复合特征在本特征集中有效特征应用参数后的实际类型，如果均未包含则返回FDummy
		template <typename T, template <typename...> class... TMP>
		using select_tpl_apply = ttPackElement<ETplsetID<TMP...>.index, TMP<T>..., FDummy>;

		/// 单个类型特征在本特征集中的索引，不存在时返回ECount
		template <typename F>
		static constexpr size_t EIndexForTyp = (pack_iter::find << ... << std::same_as<FEATURES, F>);

		/// 多个类型特征组成的复合特征在本特征集中有效特征的索引result和在复合特征中的索引index
		template <typename... F>
		static constexpr pack_iter::find_result<size_t> ETypsetID = (pack_iter::find_lt<ECount> << ... << EIndexForTyp<F>);

		/// 多个类型特征组成的复合特征在本特征集中有效特征的实际类型，如果均未包含则返回FDummy
		template <typename... F>
		using select_typ_feature = ttPackElement<ETypsetID<F...>.index, F..., FDummy>;

		/// 添加输入的特征形成新的特征集
		template <typename... NF>
		constexpr Features<FEATURES..., NF...> add(const NF &...nf) const { return data.add(nf...); }

		/// 条件成立时添加输入的特征生成新的特征集，否则返回原特征集
		template <bool CONDITION, typename... NF>
		constexpr decltype(auto) add_if(const NF &...nf) const
		{
			if constexpr (CONDITION)
				return add(nf...);
			else
				return *this;
		}

		/// 模板复合特征不存在时，添加输入的特征生成新特征集，否则返回原特征集
		template <template <typename...> class... TMP, typename... FB>
		constexpr decltype(auto) add_if_no(tplset<TMP...>, const FB &...fs) const { return add_if<!ETplsetID<TMP...>.ok>(fs...); }

		/// 类型复合特征不存在时，添加输入的特征生成新特征集，否则返回原特征集
		template <typename... F, typename... FB>
		constexpr decltype(auto) add_if_no(typset<F...>, const FB &...fs) const { return add_if<!ETypsetID<F...>.ok>(fs...); }

		/// 做为某个复合特征时的索引，当本特征集不存在指定的复合特征时返回ECount
		/// @tparam SETF复合特征，可以是单个特征F、类型特征集typset<F...>或模板特征集tplset<TMP...>
		/// @note 语义上本特征集仅应包含复合特征集中最多一个特征
		template <typename SETF>
		static constexpr size_t EIndexForAs = EIndexForTyp<SETF>; //< 复合特征是单个特征的情况

		template <typename... F>
		static constexpr size_t EIndexForAs<typset<F...>> = ETypsetID<F...>.result; //< 类型特征集特化

		template <template <typename...> class... TMP>
		static constexpr size_t EIndexForAs<tplset<TMP...>> = ETplsetID<TMP...>.result; //< 模板特征集特化

		/// 做为某个复合特征时的实际类型，当本特征集不存在指定的复合特征时返回FDummy
		/// @tparam SETF复合特征，可以是单个特征F、类型特征集typset<F...>或模板特征集tplset<TMP...>
		template <typename SETF>
		using as = ttPackElement<EIndexForAs<SETF>, FEATURES..., FDummy>;

		/// 获取某个复合特征的引用，当本特征集不存在指定的复合特征时返回FDummy的引用
		/// @tparam SETF复合特征，可以是单个特征F、类型特征集typset<F...>或模板特征集tplset<TMP...>
		/// @note 语义上本特征集仅应包含复合特征集中最多一个特征
		template <typename SETF>
		constexpr const as<SETF> &get() const
		{
			if constexpr (constexpr size_t id = EIndexForAs<SETF>; id < ECount)
				return data.template get<id>();
			else
				return _features::EDummy;
		}

	private:
		template <typename F, typename... P>
		struct _as_apply
		{
			using type = select_typ_feature<F>;
		};

		template <typename... F, typename... P>
		struct _as_apply<typset<F...>, P...>
		{
			using type = select_typ_feature<F...>;
		};

		template <template <typename...> class... TMP, typename... P>
		struct _as_apply<tplset<TMP...>, P...>
		{
			using type = ttPackElement<ETplsetID<TMP...>.index, TMP<P...>..., FDummy>;
		};

	public:
		/// 做为某个复合特征并应用指定参数包时的实际类型，当本特征集不存在指定的复合特征时返回FDummy
		/// @tparam SETF复合特征，可以是单个特征F、类型特征集typset<F...>或模板特征集tplset<TMP...>
		/// @tparam P... 要应用的参数包，当复合特征是模板特征集时有效，其它情况下丢弃
		/// @note 语义上本特征集仅应包含复合特征集中最多一个特征
		template <typename SETF, typename... P>
		using as_apply = typename _as_apply<SETF, P...>::type;
	};

	/// 特化用于使Features(const FEATURES&...fs)的形式兼容Features(const Features<FEATURES...>& fs)形式的推导和构造
	template <typename... FEATURES>
	struct Features<Features<FEATURES...>> : public Features<FEATURES...>
	{
		using Features<FEATURES...>::Features;
	};
} // namespace pw_pbl::features

namespace pw_pbl::_features
{
	using features::tplset;
	using features::typset;

	template <typename... P>
	static constexpr size_t not_dummy_cnt = (!std::same_as<FDummy, P> + ... + 0);

	template <typename SETF>
	constexpr typset<SETF> EFeatureTag = {};

	template <typename... F>
	constexpr typset<F...> EFeatureTag<typset<F...>> = {};

	template <template <typename...> class... TPL>
	constexpr tplset<TPL...> EFeatureTag<tplset<TPL...>> = {};

	template <typename DEF, typename... REAL>
	struct Sparse;

	/// 存储优化后的稀疏特征集
	/// @tparam REAL 复合特征SETF对应的最终实际存储类型，不存在时为FDummy，个数必须与SETF个数一致
	template <typename... SETF, template <typename...> class TMP, typename... REAL>
	struct Sparse<TMP<SETF...>, REAL...>
	{
		static_assert(sizeof...(REAL) == sizeof...(SETF), "store features count not equals to defined features count");

		ZoTuple<REAL...> data;

		template <typename... FEATURES>
		constexpr Sparse(const features::Features<FEATURES...> &fs) : data{{{fs.template get<SETF>()}...}}
		{
			static_assert(not_dummy_cnt<REAL...> == sizeof...(FEATURES), "some features not acceptable, or maybe some same kind features repeated");
		}

		template <size_t ID> requires(ID < sizeof...(SETF))
		static constexpr bool EIsDummy = std::same_as<FDummy, REAL...[ID]>;

		template <size_t ID> requires(ID < sizeof...(SETF))
		constexpr auto &get() const { return data.template get<ID>(); }
	};

	/// 存储优化后的稀疏特征集
	/// @tparam P 要应用到模板特征的参数包
	/// @tparam REAL 复合特征SETF对应的最终实际存储类型，不存在时为FDummy，个数必须与SETF个数一致
	template <typename... SETF, template <typename...> class TMP, typename... P, typename... REAL>
	struct Sparse<TMP<SETF...>, typset<P...>, REAL...> : public Sparse<TMP<SETF...>, REAL...>
	{
		constexpr Sparse(typset<P...>, const auto &fs) : Sparse<TMP<SETF...>, REAL...>(fs) {}
	};
} // namespace pw_pbl::_features

namespace std
{
	template <typename... SETF, template <typename...> class TMP, typename... REAL>
	struct tuple_size<pw_pbl::_features::Sparse<TMP<SETF...>, REAL...>>
	{
		static constexpr size_t value = sizeof...(REAL);
	};

	template <typename... SETF, template <typename...> class TMP, typename... P, typename... REAL>
	struct tuple_size<pw_pbl::_features::Sparse<TMP<SETF...>, pw_pbl::features::typset<P...>, REAL...>>
	{
		static constexpr size_t value = sizeof...(REAL);
	};

	template <size_t ID, typename... SETF, template <typename...> class TMP, typename... REAL>
	struct tuple_element<ID, pw_pbl::_features::Sparse<TMP<SETF...>, REAL...>>
	{
		using type = REAL...[ID];
	};

	template <size_t ID, typename... SETF, template <typename...> class TMP, typename... P, typename... REAL>
	struct tuple_element<ID, pw_pbl::_features::Sparse<TMP<SETF...>, pw_pbl::features::typset<P...>, REAL...>>
	{
		using type = REAL...[ID];
	};

	template <size_t ID, typename... SETF, template <typename...> class TMP, typename... REAL>
	constexpr const tuple_element_t<ID, pw_pbl::_features::Sparse<TMP<SETF...>, REAL...>> &get(const pw_pbl::_features::Sparse<TMP<SETF...>, REAL...> &fs) { return fs.template get<ID>(); }
} // namespace std

namespace pw_pbl::features
{
	/// 有序特征集原型，用于转换存储无序特征集，特征个数固定，如果转换时无序特征集中没有对应特征，则该特征存储为nul<N>且实际不占用存储空间
	/// @tparam SETF 复合特征，可以是单个特征F、类型特征集typset<F...>或模板特征集tplset<TMP...>
	template <typename... SETF>
	struct FeaturesProtype
	{
		/// 存储优化后的稀疏特征集
		/// @tparam REAL 复合特征SETF对应的最终实际存储类型，不存在时为FDummy，个数必须与SETF个数一致
		/// @note 支持Store<typset<P...>, REAL...>的特化形式，用于将参数包应用到模板特征中
		template <typename... REAL>
		using Sparse = _features::Sparse<FeaturesProtype, REAL...>;

		template <typename... FEATURES>
		static constexpr auto gen(const FEATURES &...fs) -> Sparse<typename Features<FEATURES...>::template as<SETF>...> { return {Features{fs...}}; }

		template <typename... P, typename... FEATURES>
		static constexpr auto gen(typset<P...> tag, const FEATURES &...fs) -> Sparse<typset<P...>, typename Features<FEATURES...>::template as_apply<SETF, P...>...> { return {tag, Features{fs...}}; }

		template <template <typename...> class OUT, typename... FEATURES>
		static constexpr auto gen(const FEATURES &...fs) -> OUT<typename Features<FEATURES...>::template as<SETF>...> { return {Sparse<typename Features<FEATURES...>::template as<SETF>...>(Features{fs...})}; }

		template <template <typename...> class OUT, typename... P, typename... FEATURES>
		static constexpr auto gen(typset<P...> tag, const FEATURES &...fs) -> OUT<P..., typename Features<FEATURES...>::template as_apply<SETF, P...>...> { return {Sparse<typset<P...>, typename Features<FEATURES...>::template as<SETF>...>(tag, Features{fs...})}; }

		/// 特征集指定特征的标记
		template <size_t ID> requires(ID < sizeof...(SETF))
		static constexpr auto F = _features::EFeatureTag<SETF...[ID]>;
	};

} // namespace pw_pbl::features

#endif // PW_PBL_FEATURES_HPP
